﻿using System;
using System.Linq;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Context;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Reflection;

namespace NMA.Infrastructure.NHibernate.Repository
{
    using NMA.Domain.Model;
    using NMA.Infrastructure.UnitOfWork;
    using NMA.Infrastructure.NHibernate.UnitOfWork;
    using NMA.Infrastructure.NHibernate.Container;
    using NMA.Infrastructure.NHibernate.DomainObject;
    using NMA.Infrastructure.DBC;    
    using NMA.Infrastructure.Logging;
    using NMA.Domain.Model.Repository;
    using NMA.Domain.Shared.Paging;
    using NMA.Infrastructure.NHibernate.Paging;

    public class CategoryRepository : BaseRepository<ICategory> ,ICategoryRepository
    {
        #region variables        

        protected IUnitOfWorkFactory UnitOfWorkFactory { get; private set; }        

        #endregion

        #region contructors        

        public CategoryRepository() : this(IoC.GetInstance<IUnitOfWorkFactory>())
        { }

        internal CategoryRepository(IUnitOfWorkFactory unitOfWorkFactory) 
        {
            UnitOfWorkFactory = unitOfWorkFactory;
        }

        #endregion

        #region implement functions        

        public IEnumerable<ICategory> All()
        {
            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                ICriteria critreria = uow.Session.CreateCriteria<Category>();
                uow.Commit();
                return critreria.List<Category>();
            }
        }

        public int CountAllCategory()
        {            
            int result = 0;

            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                // create criteria for this condition
                ICriteria criteria = uow.Session.CreateCriteria(typeof(ICategory));
                // set projection to row count
                criteria.SetProjection(Projections.RowCount());
                // get result from criteria
                result = criteria.UniqueResult<int>();
                uow.Commit();
            }

            return result;            
        }        

        public ICategory GetById(Guid id)
        {  
            ICategory category;

            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                category = uow.Session.Get<Category>(id);
                uow.Commit();
            }

            return category;                      
        }        

        public Guid Add(ICategory entity)
        {            
            object result = null;

            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                result = uow.Session.Save(entity);

                uow.Session.Flush();
                uow.Session.Clear();

                uow.Commit();
            }

            return (Guid)result;                      
        }

        public void Remove(ICategory entity)
        {            
            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                uow.Session.Delete(entity);
                uow.Commit();
            }                    
        }

        public void Remove(Guid id)
        {           
            var entity = GetById(id);

            if (entity == null)
                throw new Exception("Category's entity is null");                

            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                uow.Session.Delete(entity);
                uow.Commit();
            }
        }

        public void Update(ICategory entity)
        {            
            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                uow.Session.Update(entity);
                uow.Commit();
            }                      
        }

        public IEnumerable<ICategory> GetBy(Expression<Func<ICategory, bool>> condition)
        {           
            IEnumerable<ICategory> result = null;
            IList<ICategory> listOfCat = null;

            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                // creating criteria for this condition
                ICriteria criteria = uow.Session.CreateCriteria(typeof(ICategory));
                // getting result from criteria
                listOfCat = criteria.List<ICategory>();

                if (listOfCat == null)
                    throw new Exception("List of category is null");

                result = listOfCat.Where<ICategory>(condition.Compile());
            }

            return result;
        }

        public IPagedList<ICategory> All(int pageIndex, int pageSize)
        {
            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                var criteria = uow.Session.CreateCriteria(typeof(Category));

                return PagedList(criteria, pageIndex, pageSize);
            }
        }

        public IPagedList<ICategory> All(int pageIndex, int pageSize,
            Func<ICategory, bool> searchCriteria,
            Func<ICategory, object> orderBy,
            bool ascending)
        {
            using (var uow = UnitOfWorkFactory.GetNewUnitOfWork())
            {
                var criteria = uow.Session.CreateCriteria(typeof(Category));

                return PagedList(criteria, pageIndex, pageSize, searchCriteria, orderBy, ascending);
            }
        }

        #endregion
    }
}